iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 28
2
Modern Web

深入現代前端開發系列 第 28

Day26 前端中的效能優化

  • 分享至 

  • xImage
  •  

前言

雖然我們時常聽到盡量不要操作 DOM,但在 UI 上要完全不操作 DOM ,除非你將應用全部用 canvas 改寫,不然根本是不可能的事。

今天就來淺談前端中的效能問題。

瀏覽器的渲染過程(簡化版)

https://ithelp.ithome.com.tw/upload/images/20190929/20103565OO3nEx8rTV.jpg
圖片源自 Google Developer

這張圖相信比較有經驗的前端工程師們都看過或是類似的圖。在瀏覽器的渲染過程中主要會有這幾個階段。

其中,影響渲染效能最多的就是 layout 這個階段了。為什麼呢?假設我們今天在一個 div 上將 height: 300px 改為 height: 400px,會發生什麼事呢?因為他的高度改變了,父元素的高也可能改變、父元素的父元素的高也可能改變,追溯到最後會發現 layout 涉及的範圍會是整個 document,一旦 layout 的次數太頻繁,再加上元素內的結構複雜,就很可能有卡頓的情況發生。

既然 layout 無法避免,我們可以試著讓 layout 的次數減少一些,要讓 layout 的次數減少,我們首先要知道什麼情況下 layout 會發生:

  • 剛載入網頁時
  • 改變像是 top, bottom, margin, width, height 等會改變 layout 的 css 屬性
  • 有可見的 DOM 更新、刪除等
  • 透過 JavaScript 計算、存取 DOM 屬性,像是 offsetWidth/offsetHeight/scroll/client/getComputedStyle() 等等
  • 改變字體

如果使用 JavaScript 來存取這些屬性時,為了保證拿到的 DOM 屬性值是最新的,有可能會強制瀏覽器再次更新 layout,這通常被叫做 forced synchronous layout,如果用 Developer tool 查看,會看見紅紅的三角形。

如何減少 layout 次數?

要減少 layout 的次數有幾個方法:

  • 減少 DOM 屬性的存取:批次計算後再一次 append 到 DOM 裡面,這樣就只有一次 layout,可以透過 DocumentFragment 方便操作
  • 使用 transform 代替像是 top, bottom 的操作,讓瀏覽器幫你額外建立新的圖層
  • requestAnimationFrame:這個 API 會幫你將函式 schedule 在每個 frame 之間,並且在下一次 paint 之前執行。

transform

一般的動畫當中,如果用像是 left, right 的話,會不斷觸發 layout,造成效能降低。

如果使用 GPU 加速,browser 會幫你建立一個 layer,並且 composite,並不會觸發 layout,所以不會影響到其他元素的佈局。

不過要注意的是,因為不會影響到其他元素的佈局,代表著如果你使用 transform 的方式來實作 dropdown 的話,有可能會得到下列的結果。

要建立額外圖層,通常有幾個條件:

觸發條件

  • backface-visibility: hidden
  • will-change: transform
  • transform
  • 對 opacity 做 CSS 動畫
  • 擁有 css filter

transform 是萬靈丹嗎?

  • 太多的 layer 會導致 composite 階段時的 tree 變複雜,反而降低效能
  • layer 會佔用記憶體
  • 不支援抗鋸齒,在做 transform 時可能會變模糊

列表滾動方案

目前在無限滾動列表時,主要已經收斂成幾個方案來提升效能:

  • pagination 讓使用者點擊:使用者可以自由選擇想要到哪一頁
  • 無限滾動:透過 IntersectionObserver 來實作無限滾動,但使用者只能被動滑內容
  • windowing:只在 viewport 內顯示列表,剩下的 item 會被回收掉,像是 Android 當中的 RecyclerView 一樣,在前端要做到類似的事情有點複雜,很多東西自己算,目前最好用的是 react-window (如果用 React 的話)

相關資源

Google developer 上有一系列關於效能優化的文章值得參考:

結論

雖然還是老話一句,過早優化是萬惡的根源,但像是在 for...loop 裡面不斷呼叫 getBoundingRect 或是用 top, left 做複雜的動畫等,這些聽起來就很可怕的操作可以在事先就預防。

另外也不需要過往矯正,只要聽到要 layout 就硬要用 transform 或其他方式改寫,其實瀏覽器裡頭也做了相當多的優化,只要不要亂寫以及掌握幾個原則,還是可以在兼顧效能的情況下做開發。另外像是 React, Vue 等等內部也為了效能做了許多優化,前端開發也算是越來越幸福了...吧。


上一篇
Day27 開發直播應用要知道的事
下一篇
Day29 前端串接 Deeplink
系列文
深入現代前端開發32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言